const express =require('express') // express服务器
const logger =require('morgan'); // 日志打印
const {statusCodes}=require('http-status-codes');
const cors =require('cors'); // 跨域
const fs =require('fs-extra'); //  fs模块扩展
const path = require('path'); // path路径

const CHUNK_SIZE = 1024 * 1024 * 100


const TEMP_DIR = path.resolve( __dirname,'temp');
const PUBLIC_DIR = path.resolve(__dirname,'public')
fs.ensureDirSync(PUBLIC_DIR);//存放上传并合并好的文件
fs.ensureDirSync(TEMP_DIR);//存放分片的文件

const app=express();
app.use(logger('dev'));
app.use(cors()); // 跨域
app.use(express.json()); // 接收json数据
app.use(express.urlencoded({extended:true})); // 这个中间件，来解析 表单中的 url-encoded 格式的数据
app.use(express.static(PUBLIC_DIR)); // 托管public中的静态资源


app.post('/upload/:filename',async(req,res,next)=>{
  const { filename } = req.params;
  const { chunkFileName } = req.query;
  const start = isNaN(req.query.start) ? 0 : Number(req.query.start, 10)

  console.log(start, chunkFileName, 'chunkFileNamechunkFileName')

  // 分片目录下 当前文件的目录
  const chunkDir = path.resolve(TEMP_DIR, filename)

  // 当前接收的文件目录
  const chunkFilePath = path.resolve(chunkDir, chunkFileName)
  // 确保当前接收的分片所存的目录
  await fs.ensureDirSync(chunkDir);

  // 创建此文件的可读写流
  const ws = fs.createWriteStream(chunkFilePath, {
    start,
    flags: 'a'
  })
  req.on('aborted', () => {
    ws.close()
  })

  try {
    // 实际接收写入
    await pipeStream(req, ws);
    res.json({success:true});
  } catch (error) {
    next(error)
  }

});

function pipeStream(req, ws) {
  return new Promise((resolve, reject) => {
    req.pipe(ws);
    ws.on('finish', () => {
      resolve()
    })
    
    ws.on('error', reject)
  })
}

app.get('/merge/:filename',async(req,res,next)=>{
  // 合并
  const { filename } = req.params;
  try {
    await mergeChunks(filename, next)
    res.json({success:true});
  } catch (error) {
    next(error)
  }

})

app.get('/verify/:filename', async (req, res, next) => {
  const { filename } = req.params;
  const filepath = path.resolve(PUBLIC_DIR, filename)
  console.log(filepath, 'filepathfilepath')

  const isExist = await fs.pathExists(filepath)
  if(isExist) {
    return res.json({
      success: true,
      needUpload: false
    })
  }


  const chunkDir = path.resolve(TEMP_DIR, filename)
  const isTempExist = await fs.exists(chunkDir)
  let uploadList = []
  if(isTempExist) {
    const chunkFiles = await fs.readdir(chunkDir)
    uploadList = await Promise.all(chunkFiles.map(async (chunkFileName) => {
      const {size} = await fs.stat(path.resolve(chunkDir, chunkFileName))
      return {
        chunkFileName, 
        size
      }
    }))

  }

  return res.json({
    success: true,
    uploadList,
    needUpload: true
  })
})

function mergeChunks(filename, next) {
  return new Promise(async(resolve, reject) => {
    const chunkDir = path.resolve(TEMP_DIR, filename)
    const mergedFilePath = path.resolve(PUBLIC_DIR, filename)

    let chunkFiles = await fs.readdir(chunkDir)
    chunkFiles.sort((a, b) => Number(a.split('.')[1]) - Number(b.split('.')[1]));
    try {
      const pipes = chunkFiles.map((chunkFile, index) => {
        return pipeStream(
          fs.createReadStream(path.resolve(chunkDir, chunkFile), { autoClose: true }),
          fs.createWriteStream(mergedFilePath, { start: index * CHUNK_SIZE })
        )
      })
  
      await Promise.all(pipes)
      await fs.rmdir(chunkDir, { recursive: true })
      resolve()
    } catch (error) {
      next(error)
    }

  })
}

app.listen(8080,()=> {
  console.log('Sevär started on port 8080, http://localhost:8080')
});